package ConfigReg(ConfigReg(..), mkConfigReg, mkConfigRegU, mkConfigRegA) where

--@ \subsubsection{ConfigReg}
--@ \index{ConfigReg@\te{ConfigReg} (package,interface)|textbf}
--@ \index{mkConfigReg@\te{mkConfigReg} (module)|textbf}
--@ \index{mkConfigRegA@\te{mkConfigRegA} (module)|textbf}
--@ \index{mkConfigRegU@\te{mkConfigRegU} (module)|textbf}
--@
--@ The \te{ConfigReg} package provides a way to create configuration
--@ registers, where each update clobbers the current value, and
--@ the precise timing of updates is not important.
--@
--@ Rules which fire during the clock cycle where the register is written
--@ read a stale value (from the beginning of the clock cycle)
--@ regardless of firing order.    That is, the scheduler allows a read
--@ after a write in the rule execution order.
--@  The hardware implementation is identical for the more common
--@ registers (mkReg, mkRegU and mkRegA), and the module constructors
--@ parallel these as well.

--@
--@  The \te{ConfigReg} interface is an alias of the \te{Reg} interface
--@ \begin{libverbatim}
--@ typedef Reg#(a) ConfigReg #(type a);
--@ \end{libverbatim}
type ConfigReg a = Reg a

--@ Make a register with a given reset value.  Reset logic is synchronous.
--@ \begin{libverbatim}
--@ module mkConfigReg#(a v)(Reg#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkConfigReg :: (IsModule m c, Bits a sa) => a -> m (Reg a)
mkConfigReg v = liftModule $
  if valueOf sa == 0 then
    module
      interface
        _read = unpack 0
        _write _ = return ()
  else
    module
      _r :: VReg sa
      {-# hide #-}
      _r <- vMkConfigReg (pack v)

      let name = Valid (primGetModuleName _r)
      let t = typeOf (_ :: a)
      primSavePortType name "D_IN" t
      primSavePortType name "Q_OUT" t

      interface
        _read = unpack _r.read
        _write x = fromPrimAction (_r.write (pack x))

--@ Make a register without any reset; initial simulation value is alternating 01 bits.
--@ \begin{libverbatim}
--@ module mkConfigRegU(Reg#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkConfigRegU :: (IsModule m c, Bits a sa) => m (Reg a)
mkConfigRegU = liftModule $
  if valueOf sa == 0 then
    module
      interface
        _read = unpack 0
        _write _ = return ()
  else
    module
      _r :: VReg sa
      {-# hide #-}
      _r <- vMkConfigRegU

      let name = Valid (primGetModuleName _r)
      let t = typeOf (_ :: a)
      primSavePortType name "D_IN" t
      primSavePortType name "Q_OUT" t

      interface
        _read = unpack _r.read
        _write x = fromPrimAction (_r.write (pack x))

--@ Make a register with a given reset value.  Reset logic is asynchronous.
--@ \begin{libverbatim}
--@ module mkConfigRegA(Reg#(a))
--@   provisos (Bits#(a, sa));
--@ \end{libverbatim}
mkConfigRegA :: (IsModule m c, Bits a sa) => a ->  m (Reg a)
mkConfigRegA initValue = liftModule $
  if valueOf sa == 0 then
    module
      interface
        _read = unpack 0
        _write _ = return ()
  else
    module
      _r :: VReg sa
      {-# hide #-}
      _r <- vMkConfigRegA (pack initValue)

      let name = Valid (primGetModuleName _r)
      let t = typeOf (_ :: a)
      primSavePortType name "D_IN" t
      primSavePortType name "Q_OUT" t

      interface
        _read = unpack _r.read
        _write x = fromPrimAction (_r.write (pack x))

-- internal interfaces
interface VReg n =
    write :: Bit n -> PrimAction
    read  :: Bit n

-- internal module bvi wrappers
-- n > 0
vMkConfigReg :: Bit width -> Module (VReg width)
vMkConfigReg initval =
    module verilog "ConfigRegN"
                   (("width",valueOf width), ("init",initval)) "CLK" "RST" {
        read = "Q_OUT"{reg};
        write = "D_IN"{reg} "EN";
    } [ read <> [read, write], write << write ]

-- only for n>0
vMkConfigRegU :: Module (VReg width)
vMkConfigRegU =
    module verilog "ConfigRegUN"
                   (("width",valueOf width)) "CLK" {
        read = "Q_OUT"{reg};
        write = "D_IN"{reg} "EN";
    } [ read <> [read, write], write << write ]

-- only for n>0
vMkConfigRegA :: Bit width -> Module (VReg width)
vMkConfigRegA initval =
    module verilog "ConfigRegA"
                   (("width",valueOf width), ("init",initval)) "CLK" "RST" {
        read = "Q_OUT"{reg};
        write = "D_IN"{reg} "EN";
    } [ read <> [read, write], write << write ]

